#include "MultiNet.h"

#include "misc/Debug.h"
#include "misc/Event.h"
/*********************************************************/
void MultiNet::run()
{
	Debug& DEBUG = Debug::Instance();
	Poco::Net::SocketAddress	sender;
	Event*				event = Event::Instance(eventStr);
	int				received = 0;
	char				*buffer = new char[MULTI_PACKET_SIZE_LIMIT];
	std::string			message;

	DEBUG << "MultiNet :: Starting listening thread" << std::endl;
	mutex.lock();
	while ( !halt ) {
		mutex.unlock();

		// Read bytes from the socket if available
		received = sock->receiveFrom(buffer, MULTI_PACKET_SIZE_LIMIT, sender);
		DEBUG << "MultiNet :: Received message: " << buffer << std::endl;

		mutex.lock();
		data.push(std::string(buffer,received));
		mutex.unlock();

		event->set();
		mutex.lock();
	}
	mutex.unlock();
	DEBUG << "MultiNet :: Listening thread exiting" << std::endl;
	delete[]( buffer );
}
/*********************************************************/
void MultiNet::start(const std::string& eventStr)
{
	MultiNet::eventStr = eventStr;
	Event*		event = Event::Instance(eventStr);

	// Do nothing if the thread is allready running
	if ( lThread.isRunning() ) return;

	mutex.lock();
	halt = false;
	while ( !data.empty() ) data.pop();
	event->reset();
	mutex.unlock();
	lThread.start(*this);
	usleep(100);		// Give the thread some time to start
}
/*********************************************************/
void MultiNet::stop()
{
	// Do nothing is the thread isn't running
	if ( !lThread.isRunning() ) return;

	mutex.lock();
	// Set the halt flag
	halt = true;

	if ( !group ) {
		joinGroup(EXIT_GROUP,12345);
	}

	setLoopback(true);		// Enable loopback
	send("QUIT");			// Send a 'junk' quit message
	mutex.unlock();

	// Wait for the thread to notice the halt flag
	lThread.join();
}
/*********************************************************/
bool MultiNet::available()
{
	mutex.lock();
	bool empty = data.empty();
	mutex.unlock();
	return( !empty );
}
/*********************************************************/
std::string MultiNet::peek()
{
	mutex.lock();
	std::string msg = data.front();
	mutex.unlock();
	return( msg );
}
/*********************************************************/
std::string MultiNet::pop()
{
	mutex.lock();
	std::string	msg = data.front();
	data.pop();
	mutex.unlock();
	return( msg );
}
/*********************************************************/
void MultiNet::send(const std::string& msg)
{
	Debug& DEBUG = Debug::Instance();
	mutex.lock();
	Poco::Net::SocketAddress sender(groupip, port);
	sock->sendTo(msg.data(), msg.length(), sender);
	mutex.unlock();
	DEBUG << "MultiNet :: Sent message: " << msg << std::endl;
}
/*********************************************************/
void MultiNet::setLoopback(bool loopBack)
{
	sock->setLoopback(loopBack);
}
/*********************************************************/
int MultiNet::getGroupPort() const
{
	mutex.lock();
	int p = port;
	mutex.unlock();
	return p;
}
/*********************************************************/
std::string MultiNet::getGroupIP() const
{
	mutex.lock();
	std::string ip = groupip.toString();
	mutex.unlock();
	return( ip );
}
/*********************************************************/
void MultiNet::leaveGroup()
{
	mutex.lock();
	sock->leaveGroup(groupip, netInt);
	mutex.unlock();
}
/*********************************************************/
void MultiNet::joinGroup(const std::string& ip, unsigned int port)
{
	mutex.lock();
	MultiNet::port = port;

	// bind it to the interface
	Poco::Net::SocketAddress address(ip, port);
	sock->bind(Poco::Net::SocketAddress(Poco::Net::IPAddress(), address.port()), true);

	// Join the multicast group
	sock->joinGroup(address.host(), netInt);
	groupip = address.host();
	group = true;
	mutex.unlock();
}
/*********************************************************/
MultiNet::MultiNet(const std::string& interface)
{
	sock = 0;						// No socket has been created yet so we set it to 0
	group = false;

	// Get a networkinterface object from Poco
	netInt = Poco::Net::NetworkInterface::forName(interface);
	
	// Create the socket
	sock = new( Poco::Net::MulticastSocket );
	sock->setReusePort(true);
	sock->setReuseAddress(true);
	sock->setLoopback(false);		// Default loopback is false
}
/*********************************************************/
MultiNet::MultiNet()
{
	sock = 0;
	group = false;

	// Get an interface list from poco
	Poco::Net::NetworkInterface::NetworkInterfaceList	interface = Poco::Net::NetworkInterface::list();
	netInt = interface[0];		// First available interface ( = default )

	sock = new( Poco::Net::MulticastSocket );
	sock->setReusePort(true);
	sock->setReuseAddress(true);
	sock->setLoopback(false);		// Default loopback is false
}
/*********************************************************/
MultiNet::~MultiNet()
{
	if ( sock ) {
		sock->close();
		delete( sock );
	}
}
/*********************************************************/
